package net.sudot.chess.business.service;

import net.sudot.chess.business.dao.OrderRechargeDao;
import net.sudot.chess.business.model.Customer;
import net.sudot.chess.business.model.DepositRecord;
import net.sudot.chess.business.model.OrderLog;
import net.sudot.chess.business.model.OrderRecharge;
import net.sudot.chess.business.model.PaymentTransaction;
import net.sudot.chess.business.model.RechargeConfig;
import net.sudot.chess.business.wechat.message.RechargeTemplateMessage;
import net.sudot.chess.config.SystemProperties;
import net.sudot.chess.constant.LockConstants;
import net.sudot.chess.event.order.CancelledEvent;
import net.sudot.chess.event.order.CompletedEvent;
import net.sudot.chess.event.order.CreatedEvent;
import net.sudot.commons.lock.Lock;
import net.sudot.commons.pagination.Pageable;
import net.sudot.commons.utils.SnowflakeIdWorker;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;

/**
 * 充值订单
 *
 * @author tangjialin on 2018-03-21.
 */
@Service
public class OrderRechargeService extends BaseService<OrderRecharge> {
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Resource(name = "orderRechargeServiceSnowflakeIdWorker")
    private SnowflakeIdWorker snowflakeIdWorker;
    @Resource
    private SystemProperties systemProperties;
    @Resource
    private OrderRechargeDao orderRechargeDao;
    @Resource
    private OrderLogService orderLogService;
    @Resource
    private CustomerService customerService;
    @Resource
    private RechargeTemplateMessage rechargeTemplateMessage;

    /**
     * 获取充值订单信息
     *
     * @param sn 订单编号
     * @return 返回订单信息
     */
    public OrderRecharge findBySn(String sn) {
        return orderRechargeDao.find("sn", sn);
    }

    /**
     * 获取订单列表
     *
     * @param page   分页参数
     * @param userId 用户主键
     * @return 返回订单列表
     */
    public List<OrderRecharge> findList(Pageable page, Long userId) {
        return orderRechargeDao.findList(page, userId);
    }

    /**
     * 获取用户所有已完成的充值订单
     *
     * @param userId 用户主键
     * @return 返回用户所有已完成的充值订单
     */
    public List<OrderRecharge> findListCompleted(Long userId) {
        return orderRechargeDao.findListCompleted(userId);
    }

    /**
     * 获取所有已过期的充值订单
     *
     * @return 返回所有已过期的充值订单
     */
    public List<OrderRecharge> findListExpired() {
        return orderRechargeDao.findListExpired();
    }

    /**
     * 创建订单
     *
     * @param rechargeConfig 充值配置
     * @param customer       充值客户
     * @return 返回订单信息
     */
    @Transactional(rollbackFor = Exception.class)
    public OrderRecharge create(RechargeConfig rechargeConfig, Customer customer) {
        BigDecimal price = rechargeConfig.getPrice();
        BigDecimal money = rechargeConfig.getMoney();
        OrderRecharge order = new OrderRecharge();
        order.setUserId(customer.getId());
        order.setTarget(rechargeConfig.getId());
        order.setSn(snowflakeIdWorker.getStringId());
        order.setStatus(OrderRecharge.Status.PENDING_PAYMENT);
        order.setReceiveAmount(money);
        order.setPrice(price);
        order.setPaymentAmount(price);
        order.setPaidAmount(BigDecimal.ZERO);
        // 赠送金豆
        BigDecimal rewardMoney = Optional.ofNullable(rechargeConfig.getRewardMoney()).orElse(BigDecimal.ZERO);
        order.setExchangeAmount(rewardMoney);
        order.setIsExchangeAmount(rewardMoney.compareTo(BigDecimal.ZERO) != 0);
        // 赠送积分
        Long rewardPoint = rechargeConfig.getRewardPoint();
        rewardPoint = rewardPoint == null ? 0L : rewardPoint;
        order.setExchangePoint(rewardPoint);
        order.setIsExchangePoint(rewardPoint != 0L);
        // 过期时间
        order.setExpire(Instant.now().plus(systemProperties.getOrderExpireTime(), ChronoUnit.MINUTES));
        if (rewardMoney.compareTo(BigDecimal.ZERO) > 0) {
            order.setMemo(String.format("￥%s充值%s金豆,送%s金豆", price, money, rewardMoney));
        } else {
            order.setMemo(String.format("￥%s充值%s金豆", price, money, rewardMoney));
        }
        order = save(order);

        // 订单日志
        OrderLog orderLog = new OrderLog();
        orderLog.setType(OrderLog.Type.CREATE);
        orderLog.setOrderId(order.getId());
        orderLogService.save(orderLog);

        applicationEventPublisher.publishEvent(new CreatedEvent<OrderRecharge>(this, order, customer));
        return order;
    }

    /**
     * 订单收款
     *
     * @param order              订单
     * @param paymentTransaction 订单支付事务
     */
    @Lock(name = LockConstants.ORDER_RECHARGE, value = "#order.id")
    @Transactional(rollbackFor = Exception.class)
    public void payment(OrderRecharge order, PaymentTransaction paymentTransaction) {
        if (paymentTransaction.getAmount().compareTo(BigDecimal.ZERO) <= 0) { return; }

        order.setPaidAmount(order.getPaidAmount().add(paymentTransaction.getAmount()));
        // 订单未过期,并且订单是待支付状态,且应付金额小于等于0,则结束订单
        if (!order.hasExpired() && OrderRecharge.Status.PENDING_PAYMENT.equals(order.getStatus()) && order.getPayableAmount().compareTo(BigDecimal.ZERO) <= 0) {
            order.setStatus(OrderRecharge.Status.PAID);
            order.setExpire(null);
            complete(order);
        }
        orderRechargeDao.merge(order);

        OrderLog orderLog = new OrderLog();
        orderLog.setType(OrderLog.Type.PAYMENT);
        orderLog.setOrderId(order.getId());
        orderLogService.save(orderLog);
    }

    /**
     * 完成订单
     *
     * @param order 订单
     */
    @Lock(name = LockConstants.ORDER_RECHARGE, value = "#order.id")
    @Transactional(rollbackFor = Exception.class)
    public void complete(OrderRecharge order) {
        BigDecimal payableAmount = order.getPayableAmount();
        if (payableAmount.compareTo(BigDecimal.ZERO) > 0) {
            return;
        }
        Customer customer = customerService.find(order.getUserId());
        if (customer != null) {
            customerService.addBalance(customer, order.getReceiveAmount().add(order.getExchangeAmount()), DepositRecord.Type.RECHARGE, null);
        }
        order.setExpire(null);
        order.setStatus(OrderRecharge.Status.COMPLETED);
        order.setCompleteDate(Instant.now());
        orderRechargeDao.merge(order);

        OrderLog orderLog = new OrderLog();
        orderLog.setType(OrderLog.Type.COMPLETE);
        orderLog.setOrderId(order.getId());
        orderLogService.save(orderLog);

        applicationEventPublisher.publishEvent(new CompletedEvent<OrderRecharge>(this, order, customer));
        rechargeTemplateMessage.send(customer, order);
    }

    /**
     * 取消订单
     *
     * @param order 订单信息
     */
    @Lock(name = LockConstants.ORDER_RECHARGE, value = "#order.id")
    @Transactional(rollbackFor = Exception.class)
    public void cancel(OrderRecharge order) {
        if (order.getStatus() != OrderRecharge.Status.PENDING_PAYMENT) { return; }
        order.setExpire(null);
        order.setStatus(OrderRecharge.Status.CANCELED);
        orderRechargeDao.merge(order);

        OrderLog orderLog = new OrderLog();
        orderLog.setType(OrderLog.Type.CANCEL);
        orderLog.setOrderId(order.getId());
        orderLogService.save(orderLog);

        applicationEventPublisher.publishEvent(new CancelledEvent<OrderRecharge>(this, order, null));
    }

}
